#include "CollisionObject.h"


CollisionObject::CollisionObject()
{
    m_correction = Vector2df(0,0);
    isInWater = false;
    slantCollision = 0;
}

CollisionObject::~CollisionObject()
{
    clear();
}

//----------------------
// public declarations
//----------------------

/// General functions

Vector2df CollisionObject::calculateTotalCorrection()
{
    // Go through X-tiles
    for (int I = m_tilesX.size()-1; I >= 0; --I)
    {
        // Check if correction is greater
        if (abs(m_tilesX[I][1].x) > abs(m_correction.x))
        {
            // Store greatest correction
            m_correction.x = m_tilesX[I][1].x;
        }
    }
    for (int I = m_tilesY.size()-1; I >= 0; --I)
    {
        if (abs(m_tilesY[I][1].y) > abs(m_correction.y))
        {
            m_correction.y = m_tilesY[I][1].y;
        }
    }
    return m_correction;
}

Vector2df CollisionObject::getTotalCorrection() const
{
    return m_correction;
}

Vector2df CollisionObject::getTileX(CRuint index) const
{
    return getTile(&m_tilesX,index);
}

Vector2df CollisionObject::getTileY(CRuint index) const
{
    return getTile(&m_tilesY,index);
}

int CollisionObject::hasTileX(const Vector2df tile) const
{
    return hasTile(&m_tilesX,tile);
}

int CollisionObject::hasTileY(const Vector2df tile) const
{
    return hasTile(&m_tilesY,tile);
}

Vector2df CollisionObject::getCorrectionX(CRuint index) const
{
    return getCorrection(&m_tilesX,index);
}

Vector2df CollisionObject::getCorrectionY(CRuint index) const
{
    return getCorrection(&m_tilesY,index);
}

bool CollisionObject::getCorrection(const Vector2df tile, Vector2df &correction) const
{
    int temp = hasTileX(tile);
    if (temp >= 0)
    {
        correction = getCorrectionX(temp);
        return true;
    }
    temp = hasTileY(tile);
    if (temp >= 0)
    {
        correction = getCorrectionY(temp);
        return true;
    }
    return false;
}

int CollisionObject::getSizeX() const
{
    return m_tilesX.size();
}

int CollisionObject::getSizeY() const
{
    return m_tilesY.size();
}

void CollisionObject::addTileX(const Vector2df tile, const Vector2df correction)
{
    addTile(&m_tilesX,&m_tilesY,tile,correction);
}

void CollisionObject::addTileY(const Vector2df tile, const Vector2df correction)
{
    addTile(&m_tilesY,&m_tilesX,tile,correction);
}

void CollisionObject::clear()
{
    m_tilesX.clear();
    m_tilesY.clear();
    m_correction = Vector2df(0,0);
    isInWater = false;
    slantCollision = 0;
}


/// Analysing funtions

bool CollisionObject::isOnPlatformEdge(const Vector2df position, const Vector2df speed) const
{
    Vector2df temp;
    Vector2df pos(position+Vector2df(speed.x/abs(speed.x),1));
    if (getCorrection(pos,temp))
    {
        if (temp == Vector2df(0,0))
            return true;
        else
            return false;
    }
    return false;
}

bool CollisionObject::isOnGround(const Vector2df position) const
{
    // Very simple way to determine this
    // One could also check every tile and compare to the position, but this should suffice
    return (m_correction.y < 0);

}

bool CollisionObject::isInAir(const Vector2df position) const
{
    return (m_correction.y >= 0);
}


Vector2df CollisionObject::getCollisionTile(const Vector2df position, const SimpleDirection dir) const
{
    if (dir == diLEFT || dir == diRIGHT) // x-tile
    {
        return getNearestCollisionTile(&m_tilesX,position);
    }
    else if (dir != diNONE) // y-tile
    {
        return getNearestCollisionTile(&m_tilesY,position);
    }
    else // no preference
    {
        Vector2df tileX = getNearestCollisionTile(&m_tilesX,position);
        Vector2df tileY = getNearestCollisionTile(&m_tilesY,position);
        if (tileX == Vector2df(-1,-1))
            return tileY;
        if (tileY == Vector2df(-1,-1))
            return tileX;
        // check which one is closer
        if ((abs(tileX.x - position.x) + abs(tileX.y - position.y)) < (abs(tileY.x - position.x) + abs(tileY.y - position.y)))
            return tileX;
        else
            return tileY;
    }
}

SimpleDirection CollisionObject::getCollisionDirection(const Vector2df position) const
{
    if (m_correction.y < 0)
        return diBOTTOM;
    if (m_correction.y > 0)
        return diTOP;
    if (m_correction.x < 0)
        return diRIGHT;
    if (m_correction.x > 0)
        return diLEFT;
    return diNONE;
}

//----------------------
// private declarations
//----------------------

Vector2df CollisionObject::getTile(const vector< vector< Vector2df > > * const tiles, CRuint index) const
{
    if (index >= tiles->size())
        return Vector2df(-1,-1);
    else
        return tiles->at(index)[0];
}

int CollisionObject::hasTile(const vector< vector< Vector2df > > * const tiles, const Vector2df tile) const
{
    for (int I = tiles->size()-1; I >= 0; --I)
    {
        if (tiles->at(I)[0] == tile)
        {
            return I;
        }
    }
    return -1;
}

Vector2df CollisionObject::getCorrection(const vector< vector< Vector2df > > * const tiles, CRuint index) const
{
    if (index >= tiles->size())
    {
        return Vector2df(0,0);
    }
    else
    {
        return tiles->at(index)[1];
    }
}

void CollisionObject::addTile(vector< vector< Vector2df > > * addTiles, vector< vector< Vector2df > > * checkTiles, const Vector2df tile, const Vector2df correction) {
    // Check if already present in other group
    vector< vector< Vector2df > >::iterator I;
    for (I = checkTiles->begin(); I < checkTiles->end(); ++I)
    {
        if (I->at(0) == tile)
        {
            // If found check whether correction of stored tile is greater and erase it (less correction = better)
            if (abs(I->at(1).y) > abs(correction.x) || abs(I->at(1).x) > abs(correction.y))
            {
                //cout << "oha " << I->at(1).y << " " << correction.x << " | " << I->at(1).x << " " << correction.y << endl;
                // Add new tile (with less correction)
                addTiles->resize(addTiles->size()+1);
                addTiles->at(addTiles->size()-1).resize(2);
                addTiles->at(addTiles->size()-1)[0] = tile;
                addTiles->at(addTiles->size()-1)[1] = correction;
                // Erase old tile
                checkTiles->erase(I);
            }
            else
            {
                return;
            }
        }
    }
    // If not found, simply add the tile
    addTiles->resize(addTiles->size()+1);
    addTiles->at(addTiles->size()-1).resize(2);
    addTiles->at(addTiles->size()-1)[0] = tile;
    addTiles->at(addTiles->size()-1)[1] = correction;
}

Vector2df CollisionObject::getNearestCollisionTile(const vector< vector< Vector2df > > * tiles, const Vector2df position) const
{
    int diff = -1;
    Vector2df result = Vector2df(-1,-1);
    for (vector< vector<Vector2df> >::const_iterator I = tiles->begin(); I < tiles->end(); ++I)
    {
        if (I->at(1) != Vector2df(0,0)) // tile has a registered collision
        {
            // check whether tile is closer to position
            int currDiff = abs((I->at(0) - position).x) + abs((I->at(0) - position).y);
            if (currDiff < diff || diff < 0)
            {
                // save tile and pixel difference to player position
                diff = currDiff;
                result = I->at(0);
            }
        }
    }
    return result;
}


